package top.jingles.tools.deploytool.controller;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;

import top.jingles.tools.deploytool.entity.Args;
import top.jingles.tools.deploytool.entity.JarInfo;
import top.jingles.tools.deploytool.entity.JarInfo.JarType;
import top.jingles.tools.deploytool.entity.Task.TaskState;
import top.jingles.tools.deploytool.entity.JarList;
import top.jingles.tools.deploytool.entity.Task;
import top.jingles.tools.deploytool.entity.TaskList;
import top.jingles.utils.CommonUtil;
import top.jingles.utils.LogWriter;

public class Deploy implements HttpHandler {
    /**
     * mvn -s F:\.m2\settings.xml
     * org.apache.maven.plugins:maven-deploy-plugin:2.8.2:deploy-file
     * -Durl=http://IP:PORT/nexus/content/repositories/thirdpart
     * -DrepositoryId=thirdpart -Dfile=antlr-2.7.2.jar -DpomFile=antlr-2.7.2.pom
     * -Dpackaging=jar -DgeneratePom=false
     * -Dsources=./path/to/artifact-name-1.0-sources.jar
     * -Djavadoc=./path/to/artifact-name-1.0-javadoc.jar
     */

    // 下面这句定义用于windows下
    // public static final String BASE_CMD = "cmd /c mvn " +
    // "-s F:\\.m2\\settings.xml " +
    // "deploy:deploy-file " +
    // "-Durl=http://IP:PORT/nexus/content/repositories/thirdpart " +
    // "-DrepositoryId=thirdpart " +
    // "-DgeneratePom=false";
    // 下面这句定义用于linux下
    public static final String BASE_CMD = "%s " + "deploy:deploy-file " + "-Durl=%s " + "-DrepositoryId=%s "
            + "-DgeneratePom=false";

    public static final Runtime CMD = Runtime.getRuntime();

    public static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(10);

    Args argsObj;
    TaskList taskList;

    static LogWriter LOG = new LogWriter();

    public Deploy() {
        taskList = new TaskList();
    }

    @Override
    public void handle(HttpExchange he) throws IOException {
        String method = he.getRequestMethod();
        URI requestUri = he.getRequestURI();
        String path = requestUri.getPath();
        if (method.equalsIgnoreCase("GET") && path.equals("/tasks")) {
            Headers responseHeaders = he.getResponseHeaders();
            responseHeaders.set("Content-Type", "application/json");

            he.sendResponseHeaders(200, 0);
            // parse request
            OutputStream responseBody = he.getResponseBody();

            String jsonStr = taskList.toJSONString();
            responseBody.write(jsonStr.getBytes());
            //he.sendResponseHeaders(HttpURLConnection.HTTP_OK, response.getBytes().length);

            responseBody.close();
        }
    }
    
    public void dealAll(Args args) {
        // deploy(new File("F:\\.m2\\repository").listFiles());
        // deploy(new
        // File("/home/yanjingtu/workspace/git/centos-dockerfiles/pomtool/repository/org/apache/flink/flink-shaded-asm/5.0.4-5.0").listFiles());
        argsObj = args;

        // 初始化日志记录器
        LOG.initLogWriters(argsObj.getLogFilePath(), argsObj.getErrorLogFilePath());
        if (argsObj.isVerbose()) {
            LOG.info("开始运行！配置文件：" + argsObj.getSetting() + "，待扫描目录：" + argsObj.getPath());
        }

        // 开始干活
        dealAll(argsObj.getPath());

        // 不再接受新的任务，等待线程池中所有任务运行完成后，线程池的状态变为终止
        EXECUTOR_SERVICE.shutdown();
        // 等待，直到线程池完全终止
        while (!EXECUTOR_SERVICE.isTerminated()) {
            CommonUtil.sleep(2000);
        }
        // 终止日志记录器
        LOG.close();
    }

    public void dealAll(String basePath) {
        File file = new File(basePath);

        scanTasks(file);

        // 循环检查和启动任务
        while (true) {
            boolean allOver = true;

            for (Task task : taskList.getTasks()) {
                if (task.getState() == TaskState.TS_READY || 
                    task.getState() == TaskState.TS_FAILED) {
                    if (task.getRetryTimes() >= 3) {
                        continue;
                    }
                    allOver = false;
                    runTask(task);
                }
            }

            if (allOver) {
                break;
            }
            CommonUtil.sleep(2000);
        }
    }

    void scanTasks(File file) {
        if (file.isDirectory()) {
            File[] files = file.listFiles(f -> f.isDirectory());
            for (File f : files) {
                scanTasks(f);
            }
            files = file.listFiles(f -> f.isFile() && f.getName().endsWith(".pom"));
            for (File f : files) {
                scanTasks(f);
            }
        } else {
            // file is pom-file
            File path = file.getParentFile();
            String baseName = file.getName().substring(0, file.getName().length() - 4);
            File[] files = path.listFiles(f -> f.isFile() && 
                                          f.getName().endsWith(".jar") && 
                                          f.getName().startsWith(baseName));
            // 第二次遍历目录中的文件列表，按照classifier分类存好
            Task task = new Task(file);
            for (File f : files) {
                JarInfo info = new JarInfo(f, baseName);
                if (info.getType() == JarType.other) {
                    continue;
                }
                task.addFile(info);
            }
            taskList.addTask(task);
        }
    }

    boolean packingIsPom(File pom) {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(new FileInputStream(pom)));
            String line;
            while ((line = reader.readLine()) != null) {
                if (line.trim().indexOf("<packaging>pom</packaging>") != -1) {
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                reader.close();
            } catch (Exception e) {
            }
        }
        return false;
    }

    String getVersion(final File pom) {
        SAXReader reader = new SAXReader();
        try {
            Document document = reader.read(pom);
            // Element rootEle = document.getRootElement();
            // System.out.println("DDDDDDDDDDD" + rootEle.getName());
            // for (Element oneEle : rootEle.elements()) {
            // System.out.println("EEEEEEEEEEE:" + oneEle.getName() + ":" + oneEle.getText()
            // + ":" + oneEle.getPath() + ":" + oneEle.getNamespacePrefix());
            // }
            Node versionNode = document.selectSingleNode("/*[name()='project']/*[name()='version']");
            if (versionNode == null) {
                return "";
            }
            String versionStr = versionNode.getText();
            if (versionStr.indexOf('$') < 0) {
                return versionStr;
            }
            LOG.info("尝试推算" + versionStr + "的真实版本号");
            List<Node> propertiesNodes = document.selectNodes("/*[name()='project']/*[name()='properties']/*");
            int pos;
            int lastPos = -1;
            String lastTag = "";
            while ((pos = versionStr.indexOf('$')) >= 0) {
                int pos2 = versionStr.indexOf('}', pos);
                pos2++;
                String curTag = versionStr.substring(pos + 2, pos2 - 1);
                if (pos == lastPos && curTag.equals(lastTag)) {
                    LOG.error("推算" + versionStr + "的过程中出现无法解决的问题，程序退出！");
                    System.exit(9);
                }
                lastPos = pos;
                lastTag = curTag;
                if (argsObj.isVerbose()) {
                    LOG.info("提取出变量" + curTag);
                }
                if (curTag.equals("project.parent.version") || curTag.equals("parent.version")) {
                    Node parentVersionNode = document.selectSingleNode("/*[name()='project']/*[name()='parent']/*[name()='version']");
                    if (parentVersionNode != null) {
                        versionStr = versionStr.substring(0, pos) + parentVersionNode.getText() + versionStr.substring(pos2, versionStr.length());
                        continue;
                    }
                }
                if (propertiesNodes == null) {
                    continue;
                }
                for (Node node : propertiesNodes) {
                    if (node.getName().equals(curTag)) {
                        if (argsObj.isVerbose()) {
                            LOG.info("变量替换为" + node.getText());
                        }
                        versionStr = versionStr.substring(0, pos) + node.getText()
                                + versionStr.substring(pos2, versionStr.length());
                        break;
                    }
                }
            }
            LOG.info("推算出的版本号为：" + versionStr);
            return versionStr;
        } catch (DocumentException e) {
            // e.printStackTrace();
            LOG.error(e.getMessage());
        }
        return "";
    }

    void runTask(final Task task) {
        task.setState(TaskState.TS_RUNNING);
        task.setRetryTimes(task.getRetryTimes() + 1);
        Callable<String> subTask = () -> {
            try {
                boolean result = true;
                Map<String, JarList> jarFiles = task.getFiles();
                if (jarFiles.size() > 0) {
                    for (JarList list : jarFiles.values()) {
                        if (!deploy(task.getMainFile(), list)) {
                            result = false;
                        }
                    }
                } else {
                    result = deploy(task.getMainFile(), new JarList());
                }
                if (result) {
                    task.setState(TaskState.TS_FINISH);
                } else {
                    task.setState(TaskState.TS_FAILED);
                }
            } catch (Exception ignored) {
                task.setState(TaskState.TS_FAILED);
            }
            return "";
        };
        Future<String> future = EXECUTOR_SERVICE.submit(subTask);
        try {
            future.get(60, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            LOG.error("任务60秒超时");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            // EXECUTOR_SERVICE.shutdown();
            future.cancel(true);
        }
    }

    boolean deploy(final File pom, final JarList list) {
        final JarInfo normalJar = list.getNormalJar();
        final JarInfo javadocJar = list.getJavadocJar();
        final JarInfo sourcesJar = list.getSourcesJar();
        final JarInfo testsJar = list.getTestsJar();
        final String classifier = list.getClassifier();
        File jar = null;
        if (normalJar != null)
            jar = normalJar.getFile();
        File javadoc = null;
        if (javadocJar != null)
            javadoc = javadocJar.getFile();
        File sources = null;
        if (sourcesJar != null)
            sources = sourcesJar.getFile();
        File tests = null;
        if (testsJar != null)
            tests = testsJar.getFile();
        if (argsObj.isVerbose()) {
            if (list.hasJar()) {
                LOG.info("发布pom和关联的一个（或一组）jar：[" + pom.getPath() + "]，classifier：[" + classifier + "]");
            } else {
                LOG.info("发布pom：[" + pom.getPath() + "]");
            }
        }
        String version = getVersion(pom);
        StringBuffer cmd = new StringBuffer(String.format(BASE_CMD, argsObj.getMvnPath(), argsObj.getRepositoryUrl(), argsObj.getRepositoryId()));
        // 填充必填属性
        cmd.append(" -DpomFile=").append(pom.getName());
        if (jar != null) {
            // 当有bundle类型时，下面的配置可以保证上传的jar包后缀为.jar
            cmd.append(" -Dpackaging=jar -Dfile=").append(jar.getName());
        } else {
            cmd.append(" -Dfile=").append(pom.getName());
        }
        // 填充classifier和version
        if (classifier != null && classifier.length() > 0) {
            cmd.append(" -Dclassifier=").append(classifier);
        }
        if (version != null && version.length() > 0) {
            cmd.append(" -Dversion=").append(version);
        }
        // 填充可选文件项
        if (sources != null) {
            cmd.append(" -Dsources=").append(sources.getName());
        }
        if (javadoc != null) {
            cmd.append(" -Djavadoc=").append(javadoc.getName());
        }
        if (tests != null) {
            cmd.append(" -Dclassifiers=tests -Dtypes=test-jar -Dfiles=").append(tests.getName());
        }

        return deployOnce(pom, cmd.toString());
    }

    boolean deployOnce(final File pom, final String cmd) {
        try {
            if (argsObj.isVerbose()) {
                LOG.info("deploy命令为：" + cmd);
            }
            final Process proc = CMD.exec(cmd, null, pom.getParentFile());
            InputStream inputStream = proc.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            BufferedReader reader = new BufferedReader(inputStreamReader);
            String line;
            StringBuffer logBuffer = new StringBuffer();
            logBuffer.append("\n\n\n=======================================\n");
            while ((line = reader.readLine()) != null) {
                if (line.startsWith("[INFO]") || line.startsWith("Upload") || argsObj.isVerbose()) {
                    logBuffer.append(Thread.currentThread().getName() + " : " + line + "\n");
                }
            }
            LOG.info(logBuffer.toString());
            int result = proc.waitFor();
            if (result != 0) {
                LOG.error("上传失败：" + pom.getAbsolutePath());
                return false;
            }
        } catch (IOException e) {
            LOG.error("上传失败：" + pom.getAbsolutePath());
            e.printStackTrace();
            return false;
        } catch (InterruptedException e) {
            LOG.error("上传失败：" + pom.getAbsolutePath());
            e.printStackTrace();
            return false;
        }
        return true;
    }
}
